<template>
  <el-dialog
    style="padding: 10px 16px"
    v-model="visible"
    :show-close="false"
    :width="width"
    align-center
    :destroy-on-close="true"
    :close-on-click-modal="closeOnClickModal"
    @close="closeHandle"
  >
    <template #header="{ close, titleId, titleClass }">
      <div class="custom-header">
        <div :id="titleId" :class="titleClass + ' titleText'">
          {{ title }}
        </div>

        <div class="titleSpace"></div>

        <el-button
          style="width: 40px; height: 20px"
          v-if="!showBottom && !isDuration"
          color="#626aef"
          :icon="CloseBold"
          plain
          @click="close"
        >
        </el-button>

        <svg v-if="isDuration" width="26" height="26" viewBox="0 0 100 100">
          <!-- 背景圆环 -->
          <circle
            cx="50"
            cy="50"
            r="40"
            :fill="circleCenterColor"
            stroke="white"
            stroke-width="16"
          />
          <!-- 进度圆环 -->
          <circle
            cx="50"
            cy="50"
            r="40"
            fill="none"
            :stroke="typeCircleColor"
            stroke-width="16"
            stroke-linecap="round"
            :stroke-dasharray="circumference"
            :stroke-dashoffset="dashOffset"
            transform="rotate(-90 50 50)"
          />
        </svg>
      </div>
    </template>

    <div class="content">{{ content }}</div>

    <template #footer v-if="showBottom">
      <div>
        <el-button style="width: 120px" color="#909399" plain @click="cancel">
          cancel
        </el-button>
        <el-button style="width: 120px" color="#626aef" @click="confirm">
          confirm
        </el-button>
      </div>
    </template>
  </el-dialog>
</template>

<script setup lang="ts">
import { ref, watch, computed } from 'vue'
import { CloseBold } from '@element-plus/icons-vue'
import { MessageTypeEnum, MessageAlertProps } from './type'

// 传递给父组件的事件
const emits = defineEmits(['close'])

// 关闭事件
const closeHandle = () => {
  cancelAnimationFrame(requestAnimationFrameTimer.value)
  visible.value = false
  emits('close')
}

// 命名模型 (对应 v-model:visible) defineModel 是 Vue 3.4+ 引入的一个编译时宏
// 是否显示
const visible = defineModel<boolean>('visible', { default: false })

// 定义Props
const props = withDefaults(
  defineProps<{
    param?: MessageAlertProps
  }>(),
  {
    param: () => ({
      title: '提示',
      content: '内容',
      type: MessageTypeEnum.info,
      width: 420,
      duration: false,
      showBottom: false,
      closeOnClickModal: true,
      confirm: () => {},
      cancel: () => {},
    }),
  }
)

// 内部状态
const title = ref<string>('提示')
const content = ref<string>('内容')
const type = ref<MessageTypeEnum>(MessageTypeEnum.info)
const width = ref<number>(420)
const duration = ref<number | boolean>(3600)
const showBottom = ref<boolean>(true)
const closeOnClickModal = ref<boolean>(true)

let confirm = () => {}
let cancel = () => {}

watch(
  () => props.param,
  (newParam) => {
    // 使用空值合并操作符设置默认值
    // title.value = newParam?.title ?? '提示标题'

    // Object.assign(target, ...sources)  如果有同名属性，后面的会覆盖前面的
    // 将 sources 对象的所有属性（包括自身可枚举属性）复制到 target 对象
    // 即使 newParam 某些属性为 null/undefined，也会使用默认值
    const obj = Object.assign(
      {
        title: '提示',
        content: '内容',
        type: MessageTypeEnum.info,
        width: 420,
        duration: false,
        showBottom: false,
        closeOnClickModal: true,
        confirm: () => closeHandle(),
        cancel: () => closeHandle(),
      },
      newParam
    )

    // console.log(obj)

    title.value = obj.title
    content.value = obj.content
    type.value = obj.type
    width.value = obj.width
    duration.value = obj.duration
    showBottom.value = obj.showBottom
    closeOnClickModal.value = obj.closeOnClickModal
    confirm = obj.confirm
    cancel = obj.cancel

    setCloseTimeout()
  }
)

// 类型颜色
const typeCircleColor = computed(() => {
  switch (type.value) {
    case MessageTypeEnum.success:
      return '#67C23A'
    case MessageTypeEnum.warning:
      return '#E6A23C'
    case MessageTypeEnum.error:
      return '#F56C6C'
    case MessageTypeEnum.info:
      return '#909399'
    default:
      return '#909399'
  }
})

// 类型中心圆环颜色
const circleCenterColor = computed(() => {
  switch (type.value) {
    case MessageTypeEnum.success:
      return '#c3eaac'
    case MessageTypeEnum.warning:
      return '#f2d495'
    case MessageTypeEnum.error:
      return '#fdcbcb'
    case MessageTypeEnum.info:
      return '#e9eaeb'
    default:
      return '#e9eaeb'
  }
})

// 是否显示定时器进度条
const isDuration = computed(() => {
  if (duration.value) return true
  return typeof duration.value === 'number'
})

// 设置定时任务
const setCloseTimeout = () => {
  if (duration.value === false) return (showDurationBar.value = 'hidden')

  if (duration.value === true) return initTimer(3600)

  if (typeof duration.value === 'number') return initTimer(duration.value)

  showDurationBar.value = 'hidden'
}

// 长条形进度条
const barWidth = ref<string>('100%')
const showDurationBar = ref<'visible' | 'hidden'>('hidden')

// 圆环形进度条
const progressRatio = ref(100)
const radius = 40
const circumference = 2 * Math.PI * radius
// 计算 dash-offset
const dashOffset = computed(() => {
  return circumference - (progressRatio.value / 100) * circumference
})

// requestAnimationFrame 定时器
const requestAnimationFrameTimer = ref<number>(null)
// 设定时间
const settingTime = ref<number>(0)
// 剩余时间
const remainingTime = ref<number>(0)
// 存储上一帧的时间戳
let previousTimestamp: number = null

const initTimer = (time: number) => {
  barWidth.value = '100%'
  showDurationBar.value = 'visible'
  settingTime.value = time
  remainingTime.value = time
  previousTimestamp = 0 // 重置时间戳
  requestAnimationFrame((t) => startTimer(t))
}

// 定时任务
const startTimer = (timestamp: number) => {
  if (!previousTimestamp) previousTimestamp = timestamp

  // 计算距离上一帧的毫秒数
  const deltaTime = timestamp - previousTimestamp
  previousTimestamp = timestamp

  // 根据实际时间差更新剩余时间
  remainingTime.value = Math.max(0, remainingTime.value - deltaTime)
  if (remainingTime.value <= 0) return closeHandle()

  // 计算进度百分比
  const progress = Math.round((remainingTime.value / settingTime.value) * 100)
  barWidth.value = `${Math.max(0, progress)}%`
  progressRatio.value = progress

  // requestAnimationFrame() 的回调函数接收一个参数，表示当前帧的时间戳 ，返回动画帧ID
  requestAnimationFrameTimer.value = requestAnimationFrame(startTimer)
}
</script>

<style scoped lang="less">
.custom-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  position: relative;

  &::after {
    content: '';
    display: inline-block;
    width: 100%;
    height: 2px;
    position: absolute;
    bottom: -4px;
    background-color: #f5f7fa;
    z-index: 0;
  }

  > .titleText {
    flex: 1;
    display: flex;
    flex-direction: column;
    color: #606266;
    font-size: 16px;
    font-weight: bold;

    &::after {
      content: '';
      display: inline-block;
      width: v-bind('barWidth');
      height: 3px;
      background-color: v-bind('circleCenterColor');
      position: relative;
      bottom: -4px;
      visibility: v-bind('showDurationBar');
      /* 提示浏览器将要变化的是 width 属性 */
      will-change: width;
      /* 配合 transition 实现平滑动画 */
      transition: width 0.2s linear;
      z-index: 1;
    }
  }

  > .titleSpace {
    width: 20px;
    height: 10px;
  }
}

.content {
  font-size: 14px;
  color: #303133;
  font-weight: bold;
}
</style>
